
/* version 3.6. (c) Copyright 1993-2004 by the University of Washington.
   Written by Joseph Felsenstein, Akiko Fuseki, Sean Lamont, Andrew Keeffe,
   and Dan Fineman.
   Permission is granted to copy and use this program provided no fee is
   charged for it and provided that this copyright notice is not removed. */

#include <ctype.h>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
#include <math.h>
#include <trim_string.h>
using std::istream;
using std::string;
using std::vector;

#include "phylip.h"

void getch(Char& c, int& parens, std::istream& treefile)
{ // get next nonblank character */

    treefile >> c;
	if (treefile)
	{
		if ( c == '(' )
			parens++;
		else if ( c == ')' )
			parens--;
	}
	else
		throw std::domain_error("Premature end of New Hampshire Tree encountered.");

}  // getch */


void get_unspaced_ch(Char& c, int& parens, std::istream& treefile)
{ // get next nonblank character */

    treefile.get(c);
	if (!treefile)
		throw std::domain_error("Premature end of New Hampshire Tree encountered.");
	if (isspace(c))
		c = ' ';
	else if ( c == '(' )
		parens++;
	else if ( c == ')' )
		parens--;
}  // getch */


void processlength(double& value, Char& ch, std::istream& treefile, int& parens)
{ // read a branch length from a treefile */
	
	treefile >> value;
	getch(ch, parens, treefile);
}  // processlength */















long count_sibs (node *p)
{ // Count the number of nodes in a ring, return the total number of */
  // nodes excluding the one passed into the function (siblings)     */
	node *q;
	long return_int = 0;

	if ( p->tip )
	{
		printf ("Error: the function count_sibs called on a tip.  This is a bug.\n");
		exit (-1);
	}

	q = p->next;
	while ( q != p )
	{
		if ( q == NULL )
		{
			printf ("Error: a loop of nodes was not closed.\n");
			exit (-1);
		}
		else
		{
			return_int++;
			q = q->next;
		}
	}

	return return_int;
}  // count_sibs */





void hookup(node *p, node *q)
{ /* hook together two nodes */

	p->back = q;
	q->back = p;
}  /* hookup */






void take_name_from_tree (Char& next_ch, std::string& str, std::istream& treefile)
{
	/* This loop takes in the name from the tree.
	   Return the length of the name string.  */

	do
	{
		str += next_ch;
		treefile.get(next_ch);
		if (isspace(next_ch))
			next_ch = ' ';
	} while ( next_ch != ':' && next_ch != ',' && next_ch != ')' &&
			  next_ch != '[' && next_ch != ';');
	str = trim(str);
}  /* take_name_from_tree */


void match_names_to_data (const std::string& test_str, pointarray treenode, node **p, unsigned spp,
							const vector<string>& nayme)
{
	/* This loop matches names taken from treefile to indexed names in
	   the data file */


	for (unsigned i = 0; i < spp; ++i)
	{
		if ( test_str == nayme[i] )
		{
			*p = treenode[i];
			return;
		}
	}
	throw std::domain_error("Cannot find species: " + test_str + " in data file.");
}  /* match_names_to_data */



void addelement2(node *q, Char& ch, int& parens, istream& treefile,
				 pointarray treenode, bool lngths,
				 long *nextnode, unsigned& ntips, unsigned no_species,
					const vector<string>& nayme)
{
	/* recursive procedure adds nodes to user-defined tree
	   -- old-style bifurcating-only version */
	node *pfirst = NULL, *p;
	long current_loop_index;
	bool notlast;

	if ( ch == '(' )
	{

		current_loop_index = (*nextnode) + no_species;
		(*nextnode)++;

		/* This is an assignment of an interior node */
		p = treenode[current_loop_index];
		pfirst = p;
		notlast = true;
		while ( notlast )
		{
			/* This while loop goes through a circle (triad for
			bifurcations) of nodes */
			p = p->next;
			/* added to ensure that non base nodes in loops have indices */
			p->index = current_loop_index + 1;

			getch(ch, parens, treefile);

			addelement2(p, ch, parens, treefile, treenode, lngths,
							nextnode, ntips, no_species, nayme);

			if ( ch == ')' )
			{
				notlast = false;
				do
				{
					getch(ch, parens, treefile);
				} while ( ch != ',' && ch != ')' &&
						  ch != '[' && ch != ';' && ch != ':' );
			}
		}

	}
	else if ( ch != ')' )
	{
		std::string str;
		take_name_from_tree (ch, str, treefile);
		match_names_to_data (str, treenode, &p, no_species, nayme);
		pfirst = p;
		if ( ch == ')' )
			parens--;
		ntips++;
		p->nayme = str;
	}
	else
		getch(ch, parens, treefile);

	if ( ch == '[' )
	{	 /* getting tree weight from last comment field */
		double trweight;
		treefile >> trweight;
		if (!treefile)
			throw std::domain_error("Tree weight not found within square brackets.");
		treefile >> ch;
		if (ch != ']' )
			throw std::domain_error("Missing right square bracket.");
		treefile >> ch;
		if (ch != ';' )
			throw std::domain_error("Missing semicolon after square brackets.");
	}

	if ( q != NULL )
		hookup(q, pfirst);

	if ( ch == ':' )
	{
		// if lnghts and have length, then do not iterate over these nodes
		double valyew;
		processlength(valyew, ch, treefile, parens);
		if ( q != NULL )
		{
			if ( lngths )
			{
				q->v = valyew;
				q->back->v = q->v;
				q->iter = false;
				q->back->iter = false;
				q->back->iter = false;
			}
		}
	}

}  /* addelement2 */


void treeread2 (istream& treefile, node **root, pointarray treenode,
				bool lngths,
				unsigned no_species,
				const vector<string>& nayme)
{
	/* read in user-defined tree and set it up
	   -- old-style bifurcating-only version */
	char  ch;
	int parens = 0;
	unsigned ntips = 0;
	long nextnode;

	nextnode = 0;

	if ( !treefile )
		return;

	getch(ch, parens, treefile);

	while ( ch != '(' )
	{
		/* Eat everything in the file (i.e. digits, tabs) until you
		   encounter an open-paren */
		getch(ch, parens, treefile);
	}

	addelement2(NULL, ch, parens, treefile, treenode, lngths,
				&nextnode, ntips, no_species, nayme);
	(*root) = treenode[no_species];


	if (ntips != no_species)
		throw std::domain_error("New Hampshire Tree does not match matrix.");
	if ( parens != 0 )
		throw std::domain_error("Unmatched parenthesis in New Hampshire Tree.");
}  /* treeread2 */




